package edu.hubu.talentmanagementsystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.CaseFormat;
import edu.hubu.talentmanagementsystem.common.QnUploadUtils;
import edu.hubu.talentmanagementsystem.common.RoleType;
import edu.hubu.talentmanagementsystem.mapper.PermissionMapper;
import edu.hubu.talentmanagementsystem.mapper.StaffMapper;
import edu.hubu.talentmanagementsystem.mapper.context.MapperContext;
import edu.hubu.talentmanagementsystem.model.ext.Role;
import edu.hubu.talentmanagementsystem.model.ext.RolePermission;
import edu.hubu.talentmanagementsystem.model.ext.Resource;
import edu.hubu.talentmanagementsystem.model.ext.Staff;
import edu.hubu.talentmanagementsystem.service.IStaffService;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.apache.ibatis.annotations.Param;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import edu.hubu.talentmanagementsystem.common.EncodeOperation;
import edu.hubu.talentmanagementsystem.common.EncodeParam;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author moonlan
 * date 2021/3/2 下午8:45
 */
@SuppressWarnings(value = "unused")
@Service
@Transactional(rollbackFor = Exception.class)
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements IStaffService {

    private final MapperContext context;
    private final BCryptPasswordEncoder encoder;
    private final QnUploadUtils qnUploadUtils;

    public StaffServiceImpl(MapperContext context, BCryptPasswordEncoder encoder, QnUploadUtils qnUploadUtils) {
        this.context = context;
        this.encoder = encoder;
        this.qnUploadUtils = qnUploadUtils;
    }

    @Override
    public boolean deleteById(Integer id) {
        return context.getStaffMapper().deleteById(id) > 0;
    }

    @Override
    @EncodeOperation
    public Optional<Staff> insert(@EncodeParam Staff record) {
        int insert = context.getStaffMapper().insert(record);
        if (insert > 0) {
            return Optional.of(context.getStaffMapper().selectById(record.primaryKeyValue()));
        }
        return Optional.empty();
    }

    @Override
    public Optional<Staff> selectById(Integer id) {
        QueryWrapper<Staff> wrapper = new QueryWrapper<>();
        LambdaQueryWrapper<Staff> lambdaQueryWrapper = getColumnWrapper(wrapper.lambda()).eq(Staff::getStaffId, id);
        return Optional.of(context.getStaffMapper().selectOne(lambdaQueryWrapper));
    }

    @Override
    public List<Staff> selectAll(String order) {
        QueryWrapper<Staff> wrapper = new QueryWrapper<>();
        if (!StringUtils.hasText(order)) {
            LambdaQueryWrapper<Staff> select = getColumnWrapper(wrapper.lambda());
            return context.getStaffMapper().selectList(select);
        }
        wrapper.orderByAsc(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, order));
        LambdaQueryWrapper<Staff> select = getColumnWrapper(wrapper.lambda());
        return context.getStaffMapper().selectList(select);
    }

    @Override
    public Page<Staff> selectAllPaged(@RequestParam(required = false, defaultValue = "1") Integer currentPage,
                                      @RequestParam(required = false, defaultValue = "5") Integer pageSize,
                                      String order) {
        QueryWrapper<Staff> wrapper = new QueryWrapper<>();
        Page<Staff> page = new Page<>(currentPage, pageSize);
        if (!StringUtils.hasText(order)) {
            LambdaQueryWrapper<Staff> select = getColumnWrapper(wrapper.lambda());
            return context.getStaffMapper().selectPage(page, select);
        }
        wrapper.orderByAsc(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, order));
        LambdaQueryWrapper<Staff> select = getColumnWrapper(wrapper.lambda());
        return context.getStaffMapper().selectPage(page, select);
    }

    private LambdaQueryWrapper<Staff> getColumnWrapper(LambdaQueryWrapper<Staff> wrapper) {
        return wrapper.select(
                Staff::getStaffId
                ,
                Staff::getStaffNum
                ,
                Staff::getStaffGender
                ,
                Staff::getStaffName
                ,
                Staff::getStaffAge
                ,
                Staff::getStaffEmail
                ,
                Staff::getStaffEducationalDegree
                ,
                Staff::getStaffGraduatedSchool
                ,
                Staff::getStaffGraduatedDate
                ,
                Staff::getStaffPhoneNumber
                ,
                Staff::getStaffImgResourceId
                ,
                Staff::getStaffRoleId
                ,
                Staff::getStaffDepartmentId
                ,
                Staff::getStaffCompanyId
                ,
                Staff::getStaffIsEnable
        );
    }

    @Override
    public Page<Staff> selectLikePaged(@RequestParam(required = false, defaultValue = "1") Integer currentPage,
                                       @RequestParam(required = false, defaultValue = "5") Integer pageSize,
                                       Staff record, String order) {
        Page<Staff> page = new Page<>(currentPage, pageSize);
        QueryWrapper<Staff> wrapper = new QueryWrapper<>();
        if (!StringUtils.hasText(order)) {
            return context.getStaffMapper().selectPage(page,
                    getColumnWrapper(getColumnWrapper(getQueryWrapper(wrapper, record))));
        }
        wrapper.orderByAsc(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, order));
        return context.getStaffMapper().selectPage(page, getColumnWrapper(getQueryWrapper(wrapper, record)));
    }

    private LambdaQueryWrapper<Staff> getQueryWrapper(QueryWrapper<Staff> wrapper, Staff record) {
        LambdaQueryWrapper<Staff> lambda = wrapper.lambda();
        if (record.getStaffId() != null) {
            lambda = lambda.eq(Staff::getStaffId, record.getStaffId());
        }
        if (record.getStaffGender() != null) {
            lambda = lambda.eq(Staff::getStaffGender, record.getStaffGender());
        }
        if (record.getStaffAge() != null) {
            lambda = lambda.eq(Staff::getStaffAge, record.getStaffAge());
        }
        if (record.getStaffGraduatedDate() != null) {
            lambda = lambda.eq(Staff::getStaffGraduatedDate, record.getStaffGraduatedDate());
        }
        if (record.getStaffImgResourceId() != null) {
            lambda = lambda.eq(Staff::getStaffImgResourceId, record.getStaffImgResourceId());
        }
        if (record.getStaffRoleId() != null) {
            lambda = lambda.eq(Staff::getStaffRoleId, record.getStaffRoleId());
        }
        if (record.getStaffDepartmentId() != null) {
            lambda = lambda.eq(Staff::getStaffDepartmentId, record.getStaffDepartmentId());
        }
        if (record.getStaffCompanyId() != null) {
            lambda = lambda.eq(Staff::getStaffCompanyId, record.getStaffCompanyId());
        }
        if (record.getStaffIsEnable() != null) {
            lambda = lambda.eq(Staff::getStaffIsEnable, record.getStaffIsEnable());
        }
        if (record.getStaffNum() != null) {
            lambda = lambda.like(Staff::getStaffNum, record.getStaffNum());
        }
        if (record.getStaffPwd() != null) {
            lambda = lambda.like(Staff::getStaffPwd, record.getStaffPwd());
        }
        if (record.getStaffName() != null) {
            lambda = lambda.like(Staff::getStaffName, record.getStaffName());
        }
        if (record.getStaffEmail() != null) {
            lambda = lambda.like(Staff::getStaffEmail, record.getStaffEmail());
        }
        if (record.getStaffIdCard() != null) {
            lambda = lambda.like(Staff::getStaffIdCard, record.getStaffIdCard());
        }
        if (record.getStaffEducationalDegree() != null) {
            lambda = lambda.like(Staff::getStaffEducationalDegree, record.getStaffEducationalDegree());
        }
        if (record.getStaffGraduatedSchool() != null) {
            lambda = lambda.like(Staff::getStaffGraduatedSchool, record.getStaffGraduatedSchool());
        }
        if (record.getStaffPhoneNumber() != null) {
            lambda = lambda.like(Staff::getStaffPhoneNumber, record.getStaffPhoneNumber());
        }
        return lambda;
    }

    public int count(Staff record) {
        return context.getStaffMapper().selectCount(getQueryWrapper(new QueryWrapper<>(), record));
    }

    @Override
    @EncodeOperation
    public Optional<Staff> updateById(Integer id, @EncodeParam Staff record) {
        record.setStaffId(id);
        int update = context.getStaffMapper().updateById(record);
        if (update > 0) {
            QueryWrapper<Staff> wrapper = new QueryWrapper<>();
            LambdaQueryWrapper<Staff> lambdaQueryWrapper = getColumnWrapper(wrapper.lambda()).eq(Staff::getStaffId, id);
            return Optional.of(context.getStaffMapper().selectOne(getColumnWrapper(lambdaQueryWrapper)));
        }

        return Optional.empty();
    }

    @Override
    public Optional<Staff> findStaffInfoById(Integer staffId) {
        Staff staff = context.getStaffMapper().selectById(staffId);
        staff.setStaffPwd(null);
        staff.setResource(context.getResourceMapper().selectById(staff.getStaffImgResourceId()));
        staff.getResource().setResourceUrl(qnUploadUtils.getPrivateUrl(staff.getResource().getResourceUrl()));
        staff.setRole(context.getRoleMapper().selectById(staff.getStaffRoleId()));
        return Optional.of(staff);
    }

    @Override
    public Optional<Staff> updateByStaffNum(String staffNum, String password) {
        QueryWrapper<Staff> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("staff_num",staffNum);
        Staff staff = context.getStaffMapper().selectOne(queryWrapper);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        password = encoder.encode(password);
        staff.setStaffPwd(password);
        int update = context.getStaffMapper().updateById(staff);
        if (update > 0){
            return Optional.of(staff);
        }
        return Optional.empty();
    }


    @Override
    public Optional<Staff> selectByNum(String num) {
        LambdaQueryWrapper<Staff> wrapper = new QueryWrapper<Staff>().lambda();
        Staff staff = context.getStaffMapper().selectOne(wrapper.eq(Staff::getStaffNum, num));
        Role role = context.getRoleMapper().selectById(staff.getStaffRoleId());
        LambdaQueryWrapper<RolePermission> rolePermissionLambdaQueryWrapper =
                new QueryWrapper<RolePermission>().lambda().eq(RolePermission::getRolePermissionRoleId,
                        role.getRoleId());
        List<RolePermission> rolePermissions =
                context.getRolePermissionMapper().selectList(rolePermissionLambdaQueryWrapper);
        role.setPermissions(rolePermissions.stream().map(rolePermission -> {
            PermissionMapper permissionMapper = context.getPermissionMapper();
            return permissionMapper.selectById(rolePermission.getRolePermissionPermissionId());
        }).collect(Collectors.toList()));
        staff.setRole(role);
        return Optional.of(staff);
    }

    public boolean updatePwd(Integer id, String curPwd, String updatePwd) {
        Staff staff = context.getStaffMapper().selectById(id);
        if (!encoder.matches(curPwd, staff.getStaffPwd())) {
            throw new BadCredentialsException("认证失败");
        }

        staff.setStaffPwd(encoder.encode(updatePwd));
        return context.getStaffMapper().updateById(staff) > 0;
    }

    @Override
    public boolean register(Staff staff) {
        staff.setStaffPwd(encoder.encode(staff.getStaffPwd()));
        staff.setStaffRoleId(context.getRoleMapper()
                .selectOne(new QueryWrapper<Role>()
                        .eq("role_name", RoleType.STAFF.name())).getRoleId());
        return context.getStaffMapper().insert(staff) > 0;
    }

    @Override
    public List<Staff> selectListByCompanyId(Integer companyId) {
        return context.getStaffMapper().selectList(new QueryWrapper<Staff>().eq("staff_company_id", companyId));
    }
}
